/*
 * Copyright (c) 2003
 *	The University of Guelph.  All rights reserved.
 *
 * This software was written by Rick Macklem and was derived in part from
 * software in various BSD releases covered by the
 * subsequent copyright notices.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * It is also requested that vendors of products using all or part of this
 * software acknowledge that their products are based upon open source
 * written by Rick Macklem at the University of Guelph.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF GUELPH ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OF GUELPH BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/*	$OpenBSD: nfs_syscalls.c,v 1.12 1997/12/02 16:57:58 csapuntz Exp $	*/
/*	$NetBSD: nfs_syscalls.c,v 1.19 1996/02/18 11:53:52 fvdl Exp $	*/

/*
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)nfs_syscalls.c	8.5 (Berkeley) 3/30/95
 */

#ifndef APPLEKEXT
#include <newnfs/nfs/nfsport.h>

/* Global defs. */
#if defined(NFSD) || defined(__FreeBSD__)
extern struct nfsclienthead nfsclienthead;
extern int nfsrv_lease;
extern struct nfsrv_stablefirst nfsrv_stablefirst;
struct nfsrvfh nfs_rootfh, nfs_pubfh;
int nfs_pubfhset = 0, nfs_rootfhset = 0;
extern int newnfs_numnfsd;
#endif

extern struct nfsstats newnfsstats;
extern int nfsrv_debug;
extern u_int nfsgss_numgssd;
#endif	/* !APPLEKEXT */

#define	TRUE	1
#define	FALSE	0

/*
 * NFS server system calls
 * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
 */

/*
 * Nfs server pseudo system call for the newnfs daemons's
 * Called from the syscall routine in newnfs_port.c
 */
APPLESTATIC int
nfssvc_call(NFSPROC_T *p, struct NFSSVCARGS *uap, struct ucred *cred)
{
#if defined(NFSD) || defined(__FreeBSD__)
#ifndef APPLE
	struct nfsd_rootdirargs nrd;
	struct nfsex_args export;
	struct file *fp = NULL;
#else
	struct fileproc *fp = NULL;
#endif
	int stablefd, len;
	struct nfsd_clid adminrevoke;
	struct nfsd_dumplist dumplist;
	struct nfsd_dumpclients *dumpclients;
	struct nfsd_dumplocklist dumplocklist;
	struct nfsd_dumplocks *dumplocks;
	struct nameidata nd;
	vnode_t vp;
#endif
	int error = EINVAL;
	struct nfsd_idargs nid;

DEBUG1PRINTF("in nfssvc_call\n");
	if (NFSSCARG(uap, flag) & NFSSVC_IDNAME) {
		error = copyin(NFSSCARG(uap, argp), (caddr_t)&nid, sizeof (nid));
		if (error)
			return (error);
		error = nfssvc_idname(&nid);
		return (error);
	}
	if (NFSSCARG(uap, flag) & NFSSVC_GETSTATS) {
		error = copyout(&newnfsstats,
		    CAST_USER_ADDR_T(NFSSCARG(uap,argp)), sizeof (newnfsstats));
		return (error);
	}
	if (NFSSCARG(uap, flag) & NFSSVC_LOADV4ROOT) {
#if (!defined(NFSD) && !defined(__FreeBSD__)) || defined(APPLE)
		error = ENOSYS;
#else
		if (nfs_rootfhset)
			error = EPERM;
		else
			error = copyin(NFSSCARG(uap, argp),
			    (caddr_t)&nrd, sizeof (nrd));
		if (!error)
			error = nfsrv_loadv4root(&nrd);
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_PUBLICFH) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		NFSBZERO((caddr_t)&nfs_pubfh.nfsrvfh_data,
		    sizeof (fhandle_t));
		error = copyin(NFSSCARG(uap, argp),
		    &nfs_pubfh.nfsrvfh_data, sizeof (fhandle_t));
		if (!error)
			nfs_pubfhset = 1;
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_V4ROOTEXPORT) {
DEBUG1PRINTF("in v4ex\n");
#if (!defined(NFSD) && !defined(__FreeBSD__)) || defined(APPLE)
		error = ENOSYS;
#else
		error = copyin(NFSSCARG(uap, argp),(caddr_t)&export,
		    sizeof (struct nfsex_args));
		if (!error)
			error = nfsrv_v4rootexport(&export, cred, p);
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_NOPUBLICFH) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		nfs_pubfhset = 0;
		error = 0;
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_STABLERESTART) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		error = copyin(NFSSCARG(uap, argp), (caddr_t)&stablefd,
		    sizeof (int));
		if (!error)
			error = fp_getfvp(p, stablefd, &fp, &vp);
		if (!error && (NFSFPFLAG(fp) & (FREAD | FWRITE)) != (FREAD | FWRITE))
			error = EBADF;
		if (!error && newnfs_numnfsd != 0)
			error = EPERM;
		if (!error) {
			nfsrv_stablefirst.nsf_fp = fp;
			nfsrv_setupstable(p);
		}
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_ADMINREVOKE) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		error = copyin(NFSSCARG(uap, argp), (caddr_t)&adminrevoke,
		    sizeof (struct nfsd_clid));
		if (!error)
			error = nfsrv_adminrevoke(&adminrevoke, p);
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_DUMPCLIENTS) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		error = copyin(NFSSCARG(uap, argp), (caddr_t)&dumplist,
		    sizeof (struct nfsd_dumplist));
		if (!error && (dumplist.ndl_size < 1 ||
			dumplist.ndl_size > NFSRV_MAXDUMPLIST))
			error = EPERM;
		if (!error) {
		    len = sizeof (struct nfsd_dumpclients) * dumplist.ndl_size;
		    dumpclients = (struct nfsd_dumpclients *)malloc(len,
			M_TEMP, M_WAITOK);
		    nfsrv_dumpclients(dumpclients, dumplist.ndl_size);
		    error = copyout(dumpclients,
			CAST_USER_ADDR_T(dumplist.ndl_list), len);
		    free((caddr_t)dumpclients, M_TEMP);
		}
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_DUMPLOCKS) {
#if !defined(NFSD) && !defined(__FreeBSD__)
		error = ENOSYS;
#else
		error = copyin(NFSSCARG(uap, argp), (caddr_t)&dumplocklist,
		    sizeof (struct nfsd_dumplocklist));
		if (!error && (dumplocklist.ndllck_size < 1 ||
			dumplocklist.ndllck_size > NFSRV_MAXDUMPLIST))
			error = EPERM;
		if (!error)
			error = nfsrv_lookupfilename(&nd,
				dumplocklist.ndllck_fname, p);
		if (!error) {
			len = sizeof (struct nfsd_dumplocks) *
				dumplocklist.ndllck_size;
			dumplocks = (struct nfsd_dumplocks *)malloc(len,
				M_TEMP, M_WAITOK);
			nfsrv_dumplocks(nd.ni_vp, dumplocks,
			    dumplocklist.ndllck_size, cred, p);
			vput(nd.ni_vp);
			error = copyout(dumplocks,
			    CAST_USER_ADDR_T(dumplocklist.ndllck_list), len);
			free((caddr_t)dumplocks, M_TEMP);
		}
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_GSSDADDPORT) {
		u_short sockport;

		error = copyin(NFSSCARG(uap, argp), (caddr_t)&sockport,
		    sizeof (u_short));
		if (!error)
			error = nfsgss_addport(sockport, p);
	} else if (NFSSCARG(uap, flag) & NFSSVC_GSSCLADDPORT) {
#if !defined(NFSCL)
		error = ENOSYS;
#else
		struct nfsd_gsscl gsscl;

		error = copyin(NFSSCARG(uap, argp), (caddr_t)&gsscl,
		    sizeof (struct nfsd_gsscl));
		if (!error)
			error = nfsgsscl_addport(gsscl.ndgsscl_port,
			    gsscl.ndgsscl_uid, p);
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_GSSCLDELPORT) {
#if !defined(NFSCL)
		error = ENOSYS;
#else
		uid_t uid;

		error = copyin(NFSSCARG(uap, argp), (caddr_t)&uid,
		    sizeof (uid));
		if (!error)
			nfsgsscl_deleteport(uid);
#endif
	} else if (NFSSCARG(uap, flag) & NFSSVC_NFSUSERDPORT) {
		u_short sockport;

		error = copyin(NFSSCARG(uap, argp), (caddr_t)&sockport,
		    sizeof (u_short));
		if (!error)
			error = nfsrv_nfsuserdport(sockport, p);
	} else if (NFSSCARG(uap, flag) & NFSSVC_GSSDDELETEALL) {
		nfsgss_deleteallports(p);
		error = 0;
	} else if (NFSSCARG(uap, flag) & NFSSVC_GSSDADDFIRST) {
		if (nfsgss_numgssd == 0)
			error = 0;
		else
			error = EPERM;
	} else if (NFSSCARG(uap, flag) & NFSSVC_NFSUSERDDELPORT) {
		nfsrv_nfsuserddelport();
		error = 0;
	}
	return (error);
}

